'''
Created on May 28, 2011

@author: Santi
'''
from juegos._agents import AlphaBetaAgent
from juegos.tablut import Tablut

INFINITY = 0x7FFFFFFF

class TablutAgent(AlphaBetaAgent):
    (BLACK_AGENT, WHITE_AGENT) = (1, 2)
    def __init__(self, name="TablutAgent", horizon=1, random=None, heuristic=None):
        AlphaBetaAgent.__init__(self, name, horizon, random, heuristic)
    
    def _min_manhattan_distance(self, game, king_pos):
        ''' Retorna el minimo de las distancias manhattan hacia las
            esquinas.
        '''
        distance_to_right = 0
        for _ in xrange(king_pos[1] + 1, 9):
            distance_to_right += 1
        distance_to_left = 9 - (distance_to_right + 1)
        distance_to_bottom = 0
        for _ in xrange(king_pos[0] + 1, 9):
            distance_to_bottom += 1
        distance_to_top = 9 - (distance_to_bottom + 1)
        distance_tl = distance_to_top + distance_to_left
        distance_tr = distance_to_top + distance_to_right
        distance_bl = distance_to_bottom + distance_to_left
        distance_br = distance_to_bottom + distance_to_right
        return min(distance_tl, distance_tr, distance_bl, distance_br)
    
    def _enemy_counters_complement(self, game):
        ''' Retorna el complemento de las piezas enemigas, esto es:
            numero de piezas enemigas totales - numero de piezas enemigas en el tablero
        '''
        if game.state.current_player == Tablut.WHITE_PLAYER:
            total_enemy_counters = 16
        else:
            total_enemy_counters = 8
        enemy_counter_types = {
            Tablut.WHITE_PLAYER: Tablut.BLACK_COUNTER,
            Tablut.BLACK_PLAYER: Tablut.WHITE_COUNTER
        }
        counter_type = enemy_counter_types[game.state.current_player]
        alive_enemy_counters = len([piece for piece in game.state.pieces.values() if piece == counter_type])
        return total_enemy_counters - alive_enemy_counters
    
    def _own_pieces(self, game):
        ''' Retorna la cantidad de piezas propias en el tablero.
        '''
        counter_types = {
            Tablut.WHITE_PLAYER: Tablut.WHITE_COUNTER,
            Tablut.BLACK_PLAYER: Tablut.BLACK_COUNTER
        }
        counter_type = counter_types[game.state.current_player]
        alive_counters = len([piece for piece in game.state.pieces.values() if piece == counter_type])
        return alive_counters
    
    def _black_counters_around_king(self, game, king_pos):
        ''' Retorna la cantidad de piezas negras alrededor del rey.
        '''
        return len(game.surrounding_black_counters(king_pos))
    
    def _escape(self, game, king_pos):
        '''Retorna la cantidad de potenciales rutas de escape que tiene el rey.
        '''
        #Me aseguro que escape solo pueda ser usado por un jugador Blanco.
        if game.state.current_player == Tablut.WHITE_PLAYER: 
            pass 
        
    def _wrap(self, game, king_pos):
        '''Retorna la cantidad de piezas negras que se encuentran en la misma fila y columna del rey.
           Con esta estrategia se trata de encerrar al rey y evitar que pueda alcanzar los bordes del tablero.
        '''
        #Me aseguro que escape solo pueda ser usado por un jugador Negro.
        black_pieces = 0
        # Busco hacia la derecha.
        for i in xrange(king_pos[1]+1,9):
            new_pos = (king_pos[0], i)
            if new_pos in game.state.pieces.keys():
                if game.state.pieces[new_pos] == Tablut.BLACK_COUNTER:
                    black_pieces+=1
            
        # Busco hacia la izquierda.
        for i in xrange(king_pos[1]-1,-1,-1):
            new_pos = (king_pos[0], i)
            if new_pos in game.state.pieces.keys():
                if game.state.pieces[new_pos] == Tablut.BLACK_COUNTER:
                    black_pieces+=1
        # Busco hacia arriba.
        for i in xrange(king_pos[0]-1, -1, -1):
            new_pos = (i, king_pos[1])
            if new_pos in game.state.pieces.keys():
                if game.state.pieces[new_pos] == Tablut.BLACK_COUNTER:
                    black_pieces+=1
        # Busco hacia abajo.
        for i in xrange(king_pos[0]+1, 9):
            new_pos = (i, king_pos[1])
            if new_pos in game.state.pieces.keys():
                if game.state.pieces[new_pos] == Tablut.BLACK_COUNTER:
                    black_pieces+=1
                    
        return black_pieces
                
class BlackTablutAgent(TablutAgent):
    AMOUNT_COEFFICIENTS = 4
    DEFAULT_COEFFICIENTS = {
        "wrap": 15,
        "enemy_counters_complement": 20,
        "own_pieces": 5,
        "pieces_around_king": 20,
    }
    
    def __init__(self, coefficients=None, name='black'):
        TablutAgent.__init__(self, name)
        if coefficients:
            self.metric_coefficients = {
                "wrap": coefficients[0],                        
                "enemy_counters_complement": coefficients[1],
                "own_pieces": coefficients[2],
                "pieces_around_king": coefficients[3],
            }
        else:
            self.metric_coefficients = BlackTablutAgent.DEFAULT_COEFFICIENTS
    
    def heuristic(self, game, depth):
        for pos in game.state.pieces.keys(): 
            if game.state.pieces[pos] == Tablut.WHITE_KING:
                king_pos = pos
        summation_terms = [
            self.metric_coefficients["wrap"] * self._wrap(game, king_pos),
            self.metric_coefficients["enemy_counters_complement"] * self._enemy_counters_complement(game),
            self.metric_coefficients["own_pieces"] * self._own_pieces(game),
            self.metric_coefficients["pieces_around_king"] * self._black_counters_around_king(game, king_pos),
        ]
        return sum(summation_terms)
    
class WhiteTablutAgent(TablutAgent):
    def __init__(self, name='white'):
        self.metric_coefficients = {
            "min_manhattan_distance":-35,
            "enemy_counters_complement":-20,
            "own_pieces":-5,
            "pieces_around_king":-20,
        }
        TablutAgent.__init__(self, name)

    def heuristic(self, game, depth):
        for pos in game.state.pieces.keys():
            if game.state.pieces[pos] == Tablut.WHITE_KING:
                king_pos = pos
        summation_terms = [
            self.metric_coefficients["min_manhattan_distance"] * self._min_manhattan_distance(game, king_pos),
            self.metric_coefficients["enemy_counters_complement"] * self._enemy_counters_complement(game),
            self.metric_coefficients["own_pieces"] * self._own_pieces(game),
            self.metric_coefficients["pieces_around_king"] * self._black_counters_around_king(game, king_pos),
        ]
        return sum(summation_terms)
